﻿/*
Copyright (c) 2010 Neil Wood

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
package envisage.Controls
{
	import flash.display.DisplayObject;
	
	public class TableLayout extends Control
	{
		public static const DOCK_LEFT:uint		= 1 << 0;
		public static const DOCK_RIGHT:uint		= 1 << 1;
		public static const DOCK_TOP:uint		= 1 << 2;
		public static const DOCK_BOTTOM:uint	= 1 << 3;
		public static const DOCK_HORZ:uint	 	= DOCK_LEFT | DOCK_RIGHT;
		public static const DOCK_VERT:uint	 	= DOCK_TOP  | DOCK_BOTTOM;
		public static const DOCK_ALL:uint	 	= DOCK_HORZ | DOCK_VERT; 
		public static const DOCK_TOP_LEFT:uint	= DOCK_TOP  | DOCK_LEFT;
		
		//public static const STYLE_MAX_CONTENTS:uint  = 0;
		public static const STYLE_FIXED:uint 		 = 1;
		public static const STYLE_PERCENTAGE:uint 	 = 2;
		public static const STYLE_REMAINING:uint	 = 0;
		
		public function TableLayout()
		{
			NumRows = 1;
			NumCols = 1;
			
			super();
			
			NoScale = true;
		}
		
		public function get NumRows():int { return arrRows.length; }
		public function get NumCols():int { return arrCols.length; }
		
		public function set NumRows(value:int):void
		{
			while(arrRows.length > value ) 
				arrRows.pop();
			
			while(arrRows.length < value )
			{
				var r:Row = new Row();
				arrRows.push(r);
				
				for(var c:int = 0 ; c < arrCols.length ; c++)
					r.arrCells.push(new Cell(this, c, arrRows.length-1));
			}
			
			RequireLayout();
		}
		
		public function set NumCols(value:int):void
		{
			var r:int;
			while(arrCols.length > value ) 
			{
				arrCols.pop();
				for(r = 0 ; r < arrRows.length ; r++)
					Row(arrRows[r]).arrCells.pop();
			}
			
			while(arrCols.length < value )
			{
				arrCols.push(new Col(this));
				for(r = 0 ; r < arrRows.length ; r++)
					Row(arrRows[r]).arrCells.push(new Cell(this, arrCols.length-1, r));
			}
					
			RequireLayout();
		}
		
		private var arrCols:Array = new Array();
		private var arrRows:Array = new Array();
	
		public function SetColStyle(c:int, style:uint, operand:Number = 0, leftPadding:Number = 0, rightPadding:Number = 0):void
		{
			if( c >= NumCols ) NumCols = c+1;
			
			Col(arrCols[c]).style   = style;
			Col(arrCols[c]).operand = operand;
			Col(arrCols[c]).aPadding = leftPadding;
			Col(arrCols[c]).bPadding = rightPadding;
			RequireLayout();	
		}
		
		public function SetRowStyle(r:int, style:uint, operand:Number = 0, topPadding:Number = 0, bottomPadding:Number = 0):void
		{
			if( r >= NumRows ) NumRows = r+1;
			
			Row(arrRows[r]).style   = style;
			Row(arrRows[r]).operand = operand;
			Row(arrRows[r]).aPadding = topPadding;
			Row(arrRows[r]).bPadding = bottomPadding;
			RequireLayout();
		}
		
		private function GetCell(c:int, r:int):Cell { return Row(arrRows[r]).arrCells[c]; }
		
		public function SetCellContents(c:int, r:int, d:DisplayObject, layout:uint = 5, spanRight:int = 1, spanDown:int = 1, padding:Number = 0):void
		{
			GetCell(c, r).d = d;
			SetCellLayout(c, r, layout, spanRight, spanDown, padding);
			RequireLayout();
		}
		
		public function GetCellContents(c:int, r:int):DisplayObject
		{
			return GetCell(c, r).d;
		}
		
		public function SetCellLayout(c:int, r:int, layout:uint, spanRight:int, spanDown:int, padding:Number = 0):void
		{
			var cell:Cell = GetCell(c, r);
			cell.layout = layout;
			cell.spanRight = spanRight;
			cell.spanDown = spanDown;
			cell.padding = padding;
			RequireLayout();
		}
	
		protected override function Layout():void
		{
			LayoutLength(arrRows, height);
			LayoutLength(arrCols, width);
			
			for(var r:int = 0 ; r < arrRows.length ; r++)
				for(var c:int = 0 ; c < arrCols.length ; c++ )
					GetCell(c, r).Layout();				
		}
		
		private static var t0Array:Array = new Array();
		private function LayoutLength(arr:Array, total:Number):void
		{
			var used:Number = 0;
			var numRem:int = 0;
			var i:int;
			var len:int;
			var l:Length;
			var sr:Number;
			
			len = arr.length;
			for(i = 0 ; i < len ; i++)
			{
				l = Length(arr[i]);
				sr = l.SpaceRequired(i, total);
				t0Array.push(sr);
				if( ! isNaN(sr) ) used += sr;
				else numRem++;
			}
			
			var remaining:Number = total-used;
			remaining /= numRem;
			
			var position:Number = 0;
			for(i = 0 ; i < len ; i++)
			{
				l = Length(arr[i]);
				sr = t0Array[i];
				
				if( isNaN(sr) ) l.finalSize = remaining;
				else l.finalSize = sr;
				
				l.finalPos = position;
				position += l.finalSize;
			}
			
			t0Array.length = 0;
		}
		
		public function GetColX(col:int):Number 	{ return Col(arrCols[col]).finalPos; }
		public function GetRowY(row:int):Number 	{ return Row(arrRows[row]).finalPos; }		
		public function GetColWidth(col:int):Number { return Col(arrCols[col]).finalSize; }
		public function GetRowHeight(row:int):Number{ return Row(arrRows[row]).finalSize; }
		
	}
}

import flash.display.DisplayObject;
import envisage.Controls.TableLayout;
import flash.geom.Point;

class Length
{	
	public var style:uint;
	public var operand:Number = 0.1;
	
	public var finalSize:Number;
	public var finalPos:Number;
	
	public var aPadding:Number = 0;
	public var bPadding:Number = 0;
	
	public function SpaceRequired(index:int, total:Number):Number
	{
		if( style == TableLayout.STYLE_FIXED 		  ) return operand;
		if( style == TableLayout.STYLE_PERCENTAGE	  ) return operand * total;
		if( style == TableLayout.STYLE_REMAINING 	  ) return NaN;
		//if( style == UITableLayout.STYLE_MAX_CONTENTS ) return CalcMaxContents(index);
		throw "";
	}
	
	public function CalcMaxContents(index:int):Number { throw ""; }
}

class Row extends Length
{
	public var arrCells:Array = new Array();
	
	public override function CalcMaxContents(index:int):Number
	{
		var max:Number = 0;
		for(var i:int = 0 ; i < arrCells.length ; i++)
		{
			var c:Cell = Cell(arrCells[i]);
			var h:Number = c.d == null ? 0 : c.d.height;
			if( h > max ) max = h; 
		}
		return max;		
	}
}

class Col extends Length
{
	public function Col(table:TableLayout)
	{
		this.table = table;
	}
	
	public var table:TableLayout;
	
	public override function CalcMaxContents(index:int):Number
	{
		var max:Number = 0;
		for(var i:int = 0 ; i < table.NumRows ; i++)
		{
			var d:DisplayObject = table.GetCellContents(index, i);
			if( d != null && d.width > max ) max = d.width;
		}
		return max;
	}
}

class Cell
{
	public function Cell(table:TableLayout, col:int, row:int)
	{
		this.table = table;
		this.col = col;
		this.row = row;
	}
	
	public var table:TableLayout;
	public var col:int;
	public var row:int;
	
	public var layout:uint;
	
	public var spanRight:int;
	public var spanDown:int;
	
	public var d:DisplayObject;
	
	public var padding:Number = 0;
	
	public function Layout():void
	{
		if( d == null ) return;
		
		if( (layout & TableLayout.DOCK_HORZ) == TableLayout.DOCK_HORZ ) { DW = CW; DX = CX; }
	
		else
		{
				 if( (layout & TableLayout.DOCK_LEFT)   != 0 ) DX = CX;
			else if( (layout & TableLayout.DOCK_RIGHT ) != 0 ) DX = (CX + CW) - DW;
		    else DX = (CX + (CW/2)) - (DW/2);
		}
		
		if( (layout & TableLayout.DOCK_VERT) == TableLayout.DOCK_VERT )
		{
			DH = RH; DY = RY;
			
		}
	
		else
		{
				 if( (layout & TableLayout.DOCK_TOP)     != 0 ) DY = RY;
			else if( (layout & TableLayout.DOCK_BOTTOM ) != 0 ) DY = (RY + RH) - DH;
		    else DY = (RY + (RH/2)) - (DH/2);
		}
	}
	
	//doesn't seem to work now spanning was added.  problem in measuring lengths presumably
	public function get DX():Number 		  { return d.localToGlobal(new Point(0,0)).x; }
	public function get DY():Number 		  { return d.localToGlobal(new Point(0,0)).y; }
	public function get DW():Number 		  { return d.width; }
	public function get DH():Number 		  { return d.height; }
	public function get CX():Number			  { return table.localToGlobal(new Point(table.GetColX(col), 0)).x + padding; }
	public function get RY():Number			  { return table.localToGlobal(new Point(0, table.GetRowY(row))).y + padding; }
	//public function get CW():Number			  { return table.GetColWidth(col);  }
	//public function get RH():Number			  { return table.GetRowHeight(row); }
	public function set DX(value:Number):void { if( d.parent == null )return; d.x = d.parent.globalToLocal(new Point(value,0)).x; }
	public function set DY(value:Number):void { if( d.parent == null ) return; d.y = d.parent.globalToLocal(new Point(0,value)).y; }
	public function set DW(value:Number):void { d.width  = value; }
	public function set DH(value:Number):void { d.height = value; }
	
	public function get CW():Number
	{
		var total:Number = 0;
		for(var i:int = 0 ; i < spanRight ; i++)
			total += table.GetColWidth(col + i);
		
		total -= padding * 2;
		
		return total;
	}
	
	public function get RH():Number
	{
		var total:Number = 0;
		for(var i:int = 0 ; i < spanDown ; i++)
			total += table.GetRowHeight(row + i);
		
		total -= padding * 2;
		
		return total;
	}
}





